#include "BloqueDatosHash.h"
#include "RegistroDatos.h"
#include <stdexcept>


unsigned int BloqueDatosHash::serialize(char* &arrBytes){
	this->serInator.reset();

	// Una marca para saber donde empieza el bloque
	char firma = 'B';
	this->serInator.serializar(arrBytes, firma);

	// Se guarda el tamaño de dispersion
	this->serInator.serializar(arrBytes, this->_tamDispersion);

	// La cantidad de registros en el bloque
	int cant = this->_lRegistros.size();
	this->serInator.serializar(arrBytes, cant);

	// Uno a uno serializan los registros
	std::list<IRegistro*>::iterator it;
	for (it = this->_lRegistros.begin(); it != this->_lRegistros.end(); ++it){
		this->serInator.serializar(arrBytes, *it);
	}

	return this->serInator.getBufferSize();
}


void BloqueDatosHash::deSerialize(char* arrBytes){
	// Chequea por un error muy comun
	if (this->_lRegistros.size() != 0) throw std::runtime_error("No se puede des-serializar un bloque si este no esta vacio");

	// Inicializa el serializador
	this->serInator.reset();

	// Una marca para saber donde empieza el bloque
	char firma = 'B';
	this->serInator.desSerializar(arrBytes, firma);

	// El tamaño de dispersion
	this->serInator.desSerializar(arrBytes, this->_tamDispersion);

	// La cantidad de registros en el bloque
	int cantElementos = 0;
	this->serInator.desSerializar(arrBytes, cantElementos);

	// Los elementos del Nodo
	for (int i=0; i< cantElementos; ++i){
		IRegistro* regTemp = new RegistroDatos();

		this->serInator.desSerializar(arrBytes, regTemp);
		this->_lRegistros.push_back(regTemp);
	}
}


int BloqueDatosHash::getTamDispersion() const{
	return this->_tamDispersion;
}


int BloqueDatosHash::duplicarTamDispersion(){
	this->_tamDispersion *= 2;
	return this->_tamDispersion;
}


Resultado BloqueDatosHash::insertarRegistro(IRegistro* reg){
	std::list<IRegistro*>::iterator it;
	for (it = this->_lRegistros.begin(); it != this->_lRegistros.end(); ++it){

		if (reg->getClave() > (*it)->getClave()){
			this->_lRegistros.insert(it, reg);

			// Evalua el tamaño del bloque
			Resultado res = getEstado();

			if (res == OK) return MODIFICADO;
			else return res;

		} else if (reg->getClave() == (*it)->getClave()){
			return CLAVE_DUPLICADA;
		}
	}

	// No se encontró otro registro mayor, este es el mayor
	this->_lRegistros.push_back(reg);

	// Evalua el tamaño del bloque
	return getEstado();
}


Resultado BloqueDatosHash::eliminarRegistroPorID(const IRegistro& regID){
	std::list<IRegistro*>::iterator it;
	for (it = this->_lRegistros.begin(); it != this->_lRegistros.end(); ++it){

		if (regID.getClave() == (*it)->getClave()){
			this->_lRegistros.erase(it);

			// Evalua el estado del bloque
			Resultado res = getEstado();

			if (res == OK) return MODIFICADO;
			else return res;
		}
	}

	return NO_ENCONTRADO;
}


IRegistro& BloqueDatosHash::getRegistroPorID(const IRegistro& regID) const{
	std::list<IRegistro*>::const_iterator it;
	for (it = this->_lRegistros.begin(); it != this->_lRegistros.end(); ++it){
		if (regID.getClave() >= (*it)->getClave()){
			return **it;
		}
	}

	// No lo encontré ni tampoco uno mayor
	throw std::runtime_error("No encontrado");
	return const_cast<IRegistro&>(regID);
}



Resultado BloqueDatosHash::getEstado() const{
	// El espacio ocupado por los registros
	unsigned int tam = 0;
	std::list<IRegistro*>::const_iterator it;
	for (it = this->_lRegistros.begin(); it != this->_lRegistros.end(); ++it){
		tam += (*it)->getSize();
	}

	// La meta data del bloque (cant elementos y tamaño de dispercion)
	tam += sizeof(int) + sizeof(this->_tamDispersion);

	if (tam > IBloque::getTamBloque()){
		return OVERFLOW;

	} else if (tam <= IBloque::getTamBloque() / 2){
		return UNDERFLOW;

	} else {
		return OK;
	}
}



BloqueDatosHash::~BloqueDatosHash(){
	std::list<IRegistro*>::iterator it;
	for (it = this->_lRegistros.begin(); it != this->_lRegistros.end(); ++it){
		delete *it;
	}

	this->_lRegistros.clear();
}
